﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Microsoft.MSE.Catalog.Contracts;
using System.ServiceModel.Channels;
using System.Threading;
using System.IO;
using System.Xml;
using System.Diagnostics;
using System.ServiceModel.Description;

namespace Microsoft.MSE.Catalog.Module
{
    public class NotificationManager : INotificationManager, IExtension<ServiceHostBase>
    {
        public delegate bool FilterTestDelagate(Subscription subscription, MseEventData eventData);

        protected FilterTestDelagate mMatchesFilter = null;
        protected string mFilterDialect = null;

        private ISubscriptionManager _subscriptionManager;

        public NotificationManager(ISubscriptionManager subscriptionManager)
        {
            _subscriptionManager = subscriptionManager;
            mFilterDialect = SC.MSEEventingFilterDialect;
            mMatchesFilter = this.MatchesFilterTest;
        }
        public virtual void RaiseEvent(MseEventData eventData)
        {
            lock (this)
            {
                Dictionary<Guid, Subscription> subscriptions = _subscriptionManager.GetSubscriptions();
                Dictionary<Guid, Subscription>.KeyCollection keys = subscriptions.Keys;

                foreach (Guid key in keys)
                {
                    Subscription subscription = subscriptions[key];
                    if (mMatchesFilter(subscription, eventData))
                    {
                        Notification notification = new Notification(eventData, subscription.notifyToAddress,
                                                                    subscription.notifyToAction,
                                                                    subscription.notifyToReferenceParameters,
                                                                    subscription.notifyToBinding);
                        Thread thread = new Thread(new ThreadStart(notification.SendOneWayNotification));
                        thread.Start();
                    }
                }
            }
        }


        protected virtual bool MatchesFilterTest(Subscription subscription, MseEventData eventData)
        {
            //match the subscription filter to the current event topic
            return subscription.filter.Equals(Enum.GetName(eventData.EventTopic.GetType(), eventData.EventTopic), StringComparison.InvariantCultureIgnoreCase);
        }

        #region IExtension<ServiceHostBase> Members

        public void Attach(ServiceHostBase owner)
        {
        }

        public void Detach(ServiceHostBase owner)
        {
        }

        #endregion
    }

    public class Notification
    {
        private string mURL;
        private string mReferenceParameters;
        private string mAction;
        private Binding mBinding;
        private MseEventData mEventData;
        public Notification(MseEventData eventData, string url,
                                    string action, string referenceParameters,
                                    Binding binding)
        {
            mEventData = eventData;
            mURL = url;
            mReferenceParameters = referenceParameters;
            mAction = action;
            mBinding = binding;
        }

        private AcceptEventRequest BuildNotificationMessage()
        {
            AcceptEventRequest msg = new AcceptEventRequest(mEventData);
            return msg;
        }

        private TChannel BuildChannel<TChannel>(Binding binding, EndpointAddress endpointAddress) where TChannel : IChannel
        {
            ChannelFactory<TChannel> fac = new ChannelFactory<TChannel>(binding);
            // NZ TODO
            // We need to determine based upon the Binding if we should allow session or not

            try
            {
                fac.Endpoint.Contract.SessionMode = SessionMode.Allowed;
            }
            catch
            { }

            TChannel channel = fac.CreateChannel(endpointAddress);

            return channel;
        }

        public void SendOneWayNotification()
        {
            try
            {
                TraceHelper.TraceInformation("SendOneWayNotification Executed for URL [{0}] and Action [{1}]", mURL, mAction);

                AcceptEventRequest eventMessage = BuildNotificationMessage();
                IOutputChannel channel = BuildChannel<IOutputChannel>(mBinding, new EndpointAddress(mURL));

                TypedMessageConverter tmc = TypedMessageConverter.Create(typeof(AcceptEventRequest), mAction, SC.MSENamespace);
                Message msg = tmc.ToMessage(eventMessage, mBinding.MessageVersion);

                try
                {
                    channel.Send(msg);
                }
                finally
                {
                    channel.Close();
                }
            }
            catch (CommunicationException commEx)
            {
                TraceHelper.TraceError("SendOneWayNotification for URL [{0}] and Action [{1}] failed with communication error [{2}]", mURL, mAction, DAL.GetExceptionDetails(commEx));
            }
            catch (Exception ex)
            {
                TraceHelper.TraceError("SendOneWayNotification for URL [{0}] and Action [{1}] failed with general error [{2}]", mURL, mAction, DAL.GetExceptionDetails(ex));
            }
        }
        public void SendNotification()
        {
            try
            {
                TraceHelper.TraceInformation("SendNotification Executed for URL [{0}] and Action [{1}]", mURL, mAction);

                AcceptEventRequest eventMessage = BuildNotificationMessage();

                IRequestChannel channel = BuildChannel<IRequestChannel>(mBinding, new EndpointAddress(mURL));

                TypedMessageConverter tmc = TypedMessageConverter.Create(typeof(AcceptEventRequest), mAction, SC.MSENamespace);
                Message msg = tmc.ToMessage(eventMessage, mBinding.MessageVersion);

                try
                {
                    channel.Request(msg);
                }
                finally
                {
                    channel.Close();
                }
            }
            catch (CommunicationException commEx)
            {
                TraceHelper.TraceError("SendNotification for URL [{0}] and Action [{1}] failed with communication error [{2}]", mURL, mAction, DAL.GetExceptionDetails(commEx));
            }
            catch (Exception ex)
            {
                TraceHelper.TraceError("SendNotification for URL [{0}] and Action [{1}] failed with general error [{2}]", mURL, mAction, DAL.GetExceptionDetails(ex));
            }
        }
    }
}
